package elephant.application;

import java.io.File;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @author icecooly
 *
 */
public class ApplicationImage {
	//
	private static Logger logger=LoggerFactory.getLogger(ApplicationImage.class);
	//
	private String path;
	private String name;
	private List<URL>libraryList;
	private Map<String,String>nativeLibrary;
	private URL appJar;
	public ApplicationImage() {	
		libraryList=new ArrayList<URL>();
		nativeLibrary=new HashMap<String, String>();
	}
	//
	//
	public static ApplicationImage load(String file){
		File ff=new File(file);
		if(!ff.exists()){
			logger.warn("found app dir:"+ff.getAbsolutePath());
			return null;
		}
		ApplicationImage image=new ApplicationImage();
		image.loadOne(ff.getAbsolutePath());
		return image;
	}
	//
	public Application loadApplication()throws Exception{
		for(Class<?> clazz:getApplicationClasses()){
			Class<?>superClass=clazz.getSuperclass();
			if(superClass!=null&&superClass.equals(Application.class)){
				Application instance=
					(Application) clazz.newInstance();
				return instance;
			}
		}
		return null;
	}
	//
	public List<Class<?>>getApplicationClasses(){
		List<Class<?>>appClass=new ArrayList<Class<?>>();
		if(appJar==null){
			return appClass;
		}
		try (JarInputStream jis=new JarInputStream(
					new FileInputStream(appJar.getFile()))){
			JarEntry entry;
			ApplicationClassLoader appClassLoader=new ApplicationClassLoader(this);
			while((entry=jis.getNextJarEntry())!=null){
				String name=entry.getName();
				if(name.endsWith(".class")){
					String className=name.replace('/', '.');
					className=className.substring(0,className.length()-6);
					appClass.add(Class.forName(className,false,appClassLoader));
				}
			}
		} catch (Exception e) {
			logger.error(e.getMessage(),e);
		}
		return appClass;
	}
	//
	private static FilenameFilter jarFileNameFilter=new FilenameFilter() {
		@Override
		public boolean accept(File dir, String fileName) {
			return fileName.endsWith(".jar");
		}
	};
	//
	public void loadOne(String path){
		this.path=path;
		File appFolder=new File(path);
		name=appFolder.getName();
		File libDir=new File(appFolder,"lib");
		if(!libDir.exists()){
			logger.warn("can not find lib dir:"+libDir.getPath());
		}else{
			for(File jar:libDir.listFiles(jarFileNameFilter)){
				logger.debug("found jar:"+jar.getPath());
				if(jar.getName().startsWith(name)){//App Jar
					try {
						appJar=jar.toURI().toURL();
						logger.debug("found app jar:"+appJar.getPath());
					} catch (MalformedURLException e) {
						logger.error(e.getMessage(),e);
					}
				}else{//library
					try {
						libraryList.add(jar.toURI().toURL());
					} catch (MalformedURLException e) {
						logger.error(e.getMessage(),e);
					}
				}
			}
			//
			for(File nativeLib:libDir.listFiles(new FilenameFilter() {
				@Override
				public boolean accept(File dir, String name) {
					return name.endsWith(".so")||name.endsWith(".dll");
				}
			})){
				logger.debug("found native lib:"+nativeLib.getPath());
				nativeLibrary.put(nativeLib.getName(),nativeLib.getAbsolutePath());
			}
		}
		if(appJar==null){
			logger.warn("can not find app jar:"+name+"-[version].jar");
		}
	}
	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}
	/**
	 * @return the libraryList
	 */
	public List<URL> getLibraryList() {
		return libraryList;
	}
	/**
	 * @return the nativeLibrary
	 */
	public Map<String, String> getNativeLibrarys() {
		return nativeLibrary;
	}
	//
	public String getNativeLibrary(String name){
		return nativeLibrary.get(name);
	}
	//
	public URL[]getURLs(){
		ArrayList<URL>urls=new ArrayList<URL>();
		urls.add(appJar);
		urls.addAll(libraryList);
		return urls.toArray(new URL[urls.size()]);
	}
	/**
	 * @return the path
	 */
	public String getPath() {
		return path;
	}
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "AppImage [path=" + path + ", name=" + name + "]";
	}
	
}
